/*
 * Copyright (C) 2017 Hisilicon Limited.
 *
 * This program is free software; you can redistribute it and /or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version
 */

#ifndef _DMA_COMMON_H_
#define _DMA_COMMON_H_

#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/spinlock.h>
#include <linux/semaphore.h>
#include <linux/sched.h>

#include "dma_drv.h"
#include "nvme_drv.h"
#include "drv_log.h"
#include "devdrv_interface.h"

extern int memset_s(void *dest, size_t destMax, int c, size_t count);
extern int memcpy_s(void *dest, size_t destMax, const void *src, size_t count);
extern int strcpy_s(char *strDest, size_t destMax, const char *strSrc);
extern int strcat_s(char *strDest, size_t destMax, const char *strSrc);
extern int strncat_s(char *strDest, size_t destMax, const char *strSrc, size_t count);


#ifdef DRV_UT
#define STATIC
#else
#define STATIC static
#endif


#define DMA_DONE_BUDGET 64

#define DEVDRV_MAX_DMA_CH_SQ_DEPTH 0x1000
#define DEVDRV_MAX_DMA_CH_CQ_DEPTH 0x1000
#define DEVDRV_DMA_CH_SQ_DESC_RSV 16


#define DEVDRV_DMA_MAX_REMOTE_IRQ 128

#define DEVDRV_DMA_SQ_LDIE_ENABEL 1
#define DEVDRV_DMA_SQ_RDIE_ENABEL 2
#define DEVDRV_DMA_SQ_LDIE_AND_RDIE_ENABEL 3

/* Due to the impact of the previously unfinished dma task, the time-out period cannot
  be given based on the amount of data moved by the dma. Consider the influence of
  the PCIE bus bandwidth and the multi-channel of the DMA, giving a larger waiting time  */
#define DEVDRV_DMA_COPY_TIMEOUT (HZ * 3) /* 3s */
/* wait for dma chan SQ queue when full */
#define DEVDRV_DMA_WAIT_CHAN_AVAIL_TIMEOUT 10000

#define DEVDRV_INVALID 0x0
#define DEVDRV_VALID 0x1
#define DEVDRV_DISABLE 0
#define DEVDRV_ENABLE 1

/* 10s */
#define DEVDRV_DMA_QUERY_MAX_WAIT_TIME 10000000


#define DEVDRV_DMA_SQCQ_SIDE_BIT 0
#define DEVDRV_DMA_SML_PKT_BIT 1


#define DEVDRV_DMA_SYNC 1
#define DEVDRV_DMA_ASYNC 2

#define DEVDRV_INVALID_INSTANCE -1

#define DEVDRV_DMA_RO_RELEX_ORDER 0x2

/* DMA completion status */
enum {
    DEVDRV_DMA_SUCCESS = 0x0,
    DEVDRV_DMA_FAILED = 0x1
};

/* the side of the SQ and CQ of a DMA channel */
enum devdrv_dma_sqcq_side {
    DEVDRV_DMA_LOCAL_SIDE = 0x0,
    DEVDRV_DMA_REMOTE_SIDE = 0x1
};


#define DEVDRV_DMA_SQ_DESC_SIZE sizeof(struct devdrv_dma_sq_node)
#define DEVDRV_DMA_CQ_DESC_SIZE sizeof(struct devdrv_dma_cq_node)

struct devdrv_dma_soft_bd {
    int valid;
    int copy_type;
    int wait_type;
    int owner_bd; /* The number of the last bd sent by the chain  */
    int status;
    struct semaphore sync_sem;
    void *priv;
    u32 trans_id;
    void (*callback_func)(void *, u32, u32);
};

struct devdrv_dma_channel {
    u32 status; /* if this channel is used */
    struct device *dev;
    void __iomem *io_base; /* the base address of DMA channel */
    u32 local_id;
    u32 chan_id; /* the actual index of DMA channel in DMA controller */
    u32 flag;    /* bit0: SQ and CQ side, remote or local;
            bit1: DMA small packet is supported or not; */

    struct devdrv_dma_sq_node *sq_desc_base;
    struct devdrv_dma_cq_node *cq_desc_base;
    dma_addr_t sq_desc_dma;
    dma_addr_t cq_desc_dma;
    u32 sq_depth;
    u32 cq_depth;
    u32 sq_tail;
    u32 cq_head;
    u32 sq_head;

    struct devdrv_dma_soft_bd *dma_soft_bd;
    struct tasklet_struct dma_done_task;
    struct work_struct err_work;
    int done_irq;
    int err_irq;
    spinlock_t lock;
    spinlock_t cq_lock;
    u32 rounds;
    u32 remote_irq_cnt; /* the count of remote interrupt */
    u32 last_irq_type;
};

struct data_type_chan {
    int chan_start_id;
    int chan_num;
    int last_use_chan;
};

struct devdrv_dma_dev {
    u32 en_flg; /* disable during power sleep */
    void __iomem *io_base;
    struct device *dev;
    struct data_type_chan data_chan[DEVDRV_DMA_DATA_TYPE_MAX];
    u32 chan_count;
    u32 sq_cq_side; // DEVDRV_DMA_*_SIDE
    void *drvdata;
    struct delayed_work guard_work;
    struct devdrv_dma_channel dma_chan[0];
};

void devdrv_dma_chan_disable(struct devdrv_dma_dev *dma_dev);
struct devdrv_dma_dev *devdrv_dma_init(const char *role, void *drvdata, struct device *dev, void __iomem *io_base,
    int done_irq_base, int done_irq_num, int err_irq_base, int err_irq_num);
void devdrv_dma_exit(struct devdrv_dma_dev *dma_dev);


/* host and device both have those interfaces */
extern struct devdrv_dma_dev *devdrv_get_dma_dev(u32 dev_id);
extern u32 devdrv_get_dma_en_flg(u32 dev_id);
extern int devdrv_register_irq_func(void *drvdata, int vector_index, irqreturn_t (*callback_func)(int, void *),
    void *para, const char *name);
extern int devdrv_unregister_irq_func(void *drvdata, int vector_index, void *para);
extern int devdrv_notify_dma_err_irq(void *drvdata, u32 dma_chan_id, u32 err_irq);
extern void devdrv_dma_err_proc(struct devdrv_dma_channel *dma_chan);
extern int devdrv_check_dl_dlcmsm_state(void *drvdata);
extern void devdrv_dma_config_axim_aruser_mode(void __iomem *io_base);
extern void devdrv_set_dma_chan_en(void __iomem *io_base, u32 val);
extern void devdrv_dma_check_sram_init_status(void __iomem *io_base, unsigned long timeout);
int devdrv_dma_copy_sml_pkt(u32 dev_id, enum devdrv_dma_data_type type, dma_addr_t dst, const void *data, u32 size);


void footpoint(u32 num);
void footpointshow(void);
void footpointclear(void);
void footpoint_settoken(int token);
int footpoint_gettoken(void);
void footpointsotretolast(void);

extern void devdrv_dfx_dma_report_to_bbox(struct devdrv_dma_channel *dma_chan, u32 queue_init_sts);

#endif
